package org.activequant.dao.hibernate.types;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.activequant.core.types.TimeStamp;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;

/**
 * Hibernate helper to convert Date to/from BIGINT sql type.
 * Note that TIMESTAMP is not appropriate, because not all backends support
 * necessary time resolution (i.e. mySQL does not store milliseconds).
 * <br>
 * TimeStampType implements UserType.<br/>
 * Holds the following associated variable:
 * <ul>
 * <li>sqlTypes(int[])</li>
 * </ul>
 * <b>History:</b><br>
 *  - [20.11.2007] Created (Mike Kroutikov)<br>
 *  - [27.11.2007] Updated to switch from TIMESTAMP to BIGINT (Mike Kroutikov)<br>
 *
 *  @author Mike Kroutikov
 */
public class TimeStampType implements UserType {
	/**
	 * private final static int[] sqlTypes = new int[] {Types.BIGINT};
	 */
	private final static int [] sqlTypes = new int[] { Types.BIGINT };
	/**
	 * returns the given state(Serializable)
	 */
	public Object assemble(Serializable state, Object owner) throws HibernateException {
		return state;
	}
	/**
	 * returns the given obj(Object)
	 */
	public Object deepCopy(Object obj) throws HibernateException {
		return obj;
	}
	/**
	 * returns the given value(Object) as Serializable (cast)
	 */
	public Serializable disassemble(Object value) throws HibernateException {
		return (Serializable) value;
	}

	public boolean equals(Object x, Object y) throws HibernateException {
		if(x == y) return true;
		return (x != null && y != null && x.equals(y));
	}
	/**
	 * returns <code>value.hashCode()</code> of the given value(Object)
	 */
	public int hashCode(Object value) throws HibernateException {
		return value.hashCode();
	}
	/**
	 * returns false
	 */
	public boolean isMutable() {
		return false;
	}
	/**
	 * <strong>1.</strong> Gets the timestamp(Long) found in the given names[0](String) column in the given rs(ResultSet)<br/>
	 * <strong>2.</strong> Return a TimeStamp using the timestamp(Long) as its nanosecond value (or <code>null</code> if timestamp(Long) is null or 0L)
	 */
	public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
		Long timestamp = rs.getLong(names[0]);
		return (timestamp == null || timestamp.longValue() == 0L) ? null : new TimeStamp(timestamp.longValue());
	}
	/**
	 * <strong>1.</strong> If the given value(TimeStamp) is not null, it gets its nanoseconds(long) value and sets it in the given st(PreparedStatement) at the 
	 * given index(int) position.<br/>
	 * <strong>2.</strong> Otherwise sets a NULL BIGINT in the given st(PreparedStatement) at the given index(int) position.
	 */
	public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
		if(value != null) {
			st.setLong(index, ((TimeStamp) value).getNanoseconds());
		} else {
			st.setNull(index, Types.BIGINT);
		}
	}
	/**
	 * returns the given original(Object)
	 */
	public Object replace(Object original, Object targete, Object owner) throws HibernateException {
		return original;
	}
	/**
	 * returns the associated sqlTypes(int[]) (new int[]{Types.BIGINT})
	 */
	public int[] sqlTypes() {
		return sqlTypes;
	}
	/**
	 * returns <code>TimeStamp.class</code>
	 */
	public Class<TimeStamp> returnedClass() {
		return TimeStamp.class;
	}
}
